#pragma once
#include "event/mutex.hpp"
using namespace JHCPP::event;
#include "stdlib/bugcheck.hpp"
using namespace JHCPP::stdlib;
#include <vector>
#include <cstddef>

namespace JHCPP
{
	namespace charset
	{	
		template <typename T> 
		class TMemoryPool
		{
		public:
			TMemoryPool(std::size_t blockSize, int preAlloc, int maxAlloc)
			: m_blockSize(blockSize),
			m_allocated(preAlloc),
			m_maxAlloc(maxAlloc)
			{
				jh_assert (maxAlloc == 0 || maxAlloc >= preAlloc);
				jh_assert (preAlloc >= 0 && maxAlloc >= 0);

				int r = BLOCK_RESERVE;
				if (preAlloc > r)
					r = preAlloc;
				if (maxAlloc > 0 && maxAlloc < r)
					r = maxAlloc;
				m_blocks.reserve(r);
				for (int i = 0; i < preAlloc; ++i)
				{
					m_blocks.push_back(new T[m_blockSize]);
				}
			}

			~TMemoryPool()
			{
				for (BlockVec::iterator it = m_blocks.begin(); it != m_blocks.end(); ++it)
				{
					delete [] *it;
				}
			}

			void* get()
			{
				CMutex::ScopedLock lock(m_mutex);

				if (m_blocks.empty())
				{
					if (m_maxAlloc == 0 || m_allocated < m_maxAlloc)
					{
						++m_allocated;
						return new T[m_blockSize];
					}
					else throw OutOfMemoryException("MemoryPool exhausted");
				}
				else
				{
					T* ptr = m_blocks.back();
					m_blocks.pop_back();
					return ptr;
				}
			}

			void release(void* ptr)
			{
				CMutex::ScopedLock lock(m_mutex);

				m_blocks.push_back(reinterpret_cast<char*>(ptr));
			}

			std::size_t blockSize() const
			{
				return m_blockSize;
			}

			int allocated() const
			{
				return m_allocated;
			}

			int available() const
			{
				return (int) m_blocks.size();
			}

		private:
			TMemoryPool();
			TMemoryPool(const TMemoryPool&);
			TMemoryPool& operator = (const TMemoryPool&);

			enum
			{
				BLOCK_RESERVE = 128
			};

			typedef std::vector<T*> BlockVec;

			std::size_t m_blockSize;
			int         m_maxAlloc;
			int         m_allocated;
			BlockVec    m_blocks;
			CMutex		m_mutex;
		};
	}//end of namespace charset
}//end of namespace JHCPP
